#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>


#define LOGI printf
#define LOGE printf

//14byte文件头
typedef struct
{
    uint8_t cfType[2];//文件类型，"BM"(0x4D42)
    uint32_t cfSize;//文件大小（字节）
    uint32_t cfReserved;//保留，值为0
    uint32_t cfoffBits;//数据区相对于文件头的偏移量（字节）
}__attribute__((packed)) BITMAPFILEHEADER;
//__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐

//40byte信息头
typedef struct
{
    uint8_t ciSize[4];//BITMAPFILEHEADER所占的字节数
    uint32_t ciWidth;//宽度
    uint32_t ciHeight;//高度
    uint8_t ciPlanes[2];//目标设备的位平面数，值为1
    uint32_t ciBitCount;//每个像素的位数
    uint8_t ciCompress[4];//压缩说明
    uint8_t ciSizeImage[4];//用字节表示的图像大小，该数据必须是4的倍数
    uint8_t ciXPelsPerMeter[4];//目标设备的水平像素数/米
    uint8_t ciYPelsPerMeter[4];//目标设备的垂直像素数/米
    uint8_t ciClrUsed[4]; //位图使用调色板的颜色数
    uint8_t ciClrImportant[4]; //指定重要的颜色数，当该域的值等于颜色数时（或者等于0时），表示所有颜色都一样重要
}__attribute__((packed)) BITMAPINFOHEADER;

typedef struct
{
    uint8_t blue;
    uint8_t green;
    uint8_t red;
    uint8_t reserved;
}__attribute__((packed)) PIXEL;//颜色模式RGB

BITMAPFILEHEADER FileHead;
BITMAPINFOHEADER InfoHead;

struct buffer_object {
    uint32_t width;
    uint32_t height;
    uint32_t pitch;
    uint32_t handle;
    uint32_t size;
    uint8_t *vaddr;
    uint32_t fb_id;
};

struct buffer_object buf;

static int turn_180(char *s,int len ,int bit)
{
    for(int i = 0, j = len-bit; i < j;) {

        for(int n = 0 ; n < bit; n ++)
        {
        char c = s[i+n];

        s[i+n] = s[j+n];

        s[j+n] = c;
        }
        i = i + bit;
        j = j - bit;
    }
    return 0;
}


static int get_bmp_buffer(char *bmp_buf)
{

    FILE *fp;
    int rc;
    int flen = 0;

    LOGI("into show_bmp function_____________\n");

    LOGI("path = %s", "./1.bmp");
    fp = fopen( "./1.bmp", "rb" );
    if(fp == NULL){
        LOGE("load > cursor file open failed");
        return -1;
    }
    /* 求解文件长度 */
    fseek(fp,0,SEEK_SET);
    fseek(fp,0,SEEK_END);
    flen = ftell(fp);


    /* 再移位到文件头部 */
    fseek(fp,0,SEEK_SET);

    rc = fread(&FileHead, sizeof(BITMAPFILEHEADER),1, fp);
    if ( rc != 1)
    {
        LOGI("read header error!\n");
        fclose( fp );
        return( -2 );
    }
    //检测是否是bmp图像
    if (memcmp(FileHead.cfType, "BM", 2) != 0)
    {
        LOGI("it's not a BMP file\n");
        fclose( fp );
        return( -3 );
    }
    rc = fread( (char *)&InfoHead, sizeof(BITMAPINFOHEADER),1, fp );
    if ( rc != 1)
    {
        LOGI("read infoheader error!\n");
        fclose( fp );
        return( -4 );
    }

    LOGI("ciWidth: %d\n",InfoHead.ciWidth);
    //跳转的数据区
    fseek(fp, FileHead.cfoffBits, SEEK_SET);

    printf("flen: %d\n",flen);
    bmp_buf = (char *)malloc(flen - 54);
    if(bmp_buf == NULL){
        LOGE("load > malloc bmp out of memory!");
        return -1;
    }

    rc = fread( (char *)bmp_buf,flen-54,1, fp );
    
    return 0;
}

static int modeset_create_fb(int fd, struct buffer_object *bo)
{
    struct drm_mode_create_dumb create = {};
    struct drm_mode_map_dumb map = {};

    create.width = bo->width;
    create.height = bo->height;
    create.bpp = 32;
    drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);

    bo->pitch = create.pitch;
    bo->size = create.size;
    bo->handle = create.handle;
    drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
            bo->handle, &bo->fb_id);

    map.handle = create.handle;
    drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

    printf("creat.size:%llu,map.offset:%llu\n",create.size,map.offset);
    bo->vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,
            MAP_SHARED, fd, map.offset);

    //memset(bo->vaddr, 0xff,bo->size);

    char *bmp_buf;
    get_bmp_buffer(bmp_buf);
    
    memcpy(bo->vaddr,(char *)bmp_buf,bo->size);
    turn_180(bmp_buf,614400 ,4);
    return 0;
}

static void modeset_destroy_fb(int fd, struct buffer_object *bo)
{
    struct drm_mode_destroy_dumb destroy = {};

    drmModeRmFB(fd, bo->fb_id);

    munmap(bo->vaddr, bo->size);

    destroy.handle = bo->handle;
    drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}


int main(int argc, char **argv)
{
    int fd;
    drmModeConnector *conn;
    drmModeRes *res;
    drmModePlaneRes *plane_res;
    uint32_t conn_id;
    uint32_t crtc_id;
    uint32_t plane_id;

    fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);

    res = drmModeGetResources(fd);
    crtc_id = res->crtcs[0];
    conn_id = res->connectors[0];

    drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
    plane_res = drmModeGetPlaneResources(fd);
    plane_id = plane_res->planes[0];

    conn = drmModeGetConnector(fd, conn_id);
    buf.width = conn->modes[0].hdisplay;
    buf.height = conn->modes[0].vdisplay;

    modeset_create_fb(fd, &buf);


    drmModeSetCrtc(fd, crtc_id, buf.fb_id,
            0, 0, &conn_id, 1, &conn->modes[0]);

    getchar();

    /* crop the rect from framebuffer(100, 150) to crtc(50, 50) */
    drmModeSetPlane(fd, plane_id, crtc_id, buf.fb_id, 0,
            50, 50, 320, 320,
            100 << 16, 150 << 16, 320 << 16, 320 << 16);

    getchar();

    modeset_destroy_fb(fd, &buf);

    drmModeFreeConnector(conn);
    drmModeFreePlaneResources(plane_res);
    drmModeFreeResources(res);

    close(fd);

    return 0;
}
